# encoding=utf-8

"""SerialHost

Frame:
    1: start signal
    2 - 5: pack_index
    6: len
    7 - x: data
    x + 1: crc

Rate(15cm, 6000000b):
     64B: 57.804    kB/s
    128B: 103.034   KB/s  -- recommend
    192B: 144.260   KB/s  -- recommend
    256B: 176.853   KB/s

Rate(1m, 6000000b):
    64B: 57.981     KB/s
   128B: 100.597    KB/s
   192B: 147.736    KB/s
   256B: 179.178    KB/s
"""
import time
import traceback
import logging
from SerialBase import SerialBase
from SerialTools import *

# logging.basicConfig(level = logging.INFO,format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s')

class SerialSend(SerialBase):
    def __init__(self, device: str, rate: int = 115200, package_size: int = 64):
        super().__init__(device, rate)
        # size for one frame
        self.package_size = package_size
        self.body_size = self.package_size - 6 - 1

    def send_raw_file(self, file_path: str):
        raw_bytearray = bytearray(compress_file(file_path))

        start_time = time.perf_counter()
        res = self._send_buffer(raw_bytearray, len(raw_bytearray), self.body_size)
        end_time = time.perf_counter()

        data_size = 1 * len(raw_bytearray) / 1e3  # Byte --1e3--> KB --1e3--> MB
        cost = end_time - start_time
        print("time cost in {}, about {}KB/s".format(cost, data_size / cost))
        logging.info("Send status: {}".format(res))
        return res

    ####   Basic  buffer send tools   ####
    def _send_buffer(self, buffer, buffer_len: int, body_size: int):
        self._clean_serial_cache()

        package_index = 0
        start_idx = 0

        while start_idx < buffer_len:
            # index
            if buffer_len - start_idx >= body_size:
                next_idx = start_idx + body_size
            else:
                next_idx = start_idx + (buffer_len - start_idx)

            # frame
            logging.info("Send info, package:{}, start: {}, end:{}".format(package_index, start_idx, next_idx))
            now_frame = self._make_send_array(buffer, package_index, start_idx, next_idx)

            # try send
            send_res = self._try_send_frame(now_frame)
            if not send_res:
                return False

            # increase
            start_idx = next_idx
            package_index += 1

        return True

    def _try_send_frame(self, frame: bytes):
        error_msg = ""
        for i in range(3):
            ok = self._send_raw_data(frame)
            if ok:
                response = int.from_bytes(self._read_raw_data(1), 'big')
                if response == self.ACK:
                    error_msg = ""
                    break
                else:
                    error_msg = "frame send fail in recv crc"
            else:
                error_msg = "frame send fail in host port"

            time.sleep(0.1)

        if not error_msg == "":
            logging.error(error_msg)
            return False

        return True

    def _make_send_array(self,
                         buffer,
                         package_index: int,
                         start_idx: int,
                         end_idx: int):
        frame = bytearray()
        # 1: start signal
        frame.append(0x00)
        # 2 - 5: pack_index
        for i in range(4):
            uint8_index = (package_index >> (8 * i)) & 0xff
            frame.append(uint8_index)
        # 6: len
        frame.append(end_idx - start_idx)
        # 7 - buffer_size - 1: data
        for i in range(start_idx, end_idx):
            frame.append(buffer[i])
        # buffer_size: crc
        frame.append(self._crc8(frame))
        # debug
        logging.info("Index: {}, buffer is: {}".format(package_index, frame))

        return bytes(frame)

if __name__ == '__main__':
    host = SerialSend("/dev/ttyACM0", 6000000, 256)
    print(host.send_raw_file("sample-data/sample-image.jpg"))
